/*
 * This file is part of Ember.
 *
 * Ember is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Ember is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Ember.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (C) Grant Tyra 2010
 * granttyra@gmail.com
*/

#include "ember_types.h"
#include "ember_statement.h"
#include "ember_call_stack.h"
#include "ember_string.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

EmberObject *emberNull = NULL;

EmberObject *EmberNull() {
	EmberObjectRetain(emberNull);
	return emberNull;
}

EmberObject *EmberObjectCreateString(const char *expr) {
    EmberObject *obj = EmberObjectCreate("String");
	char *string = EmberStringCopy(expr);
	obj->typeFlags = TYPE_STRING;
    EmberDictionaryPut(obj->attributes, "value", string);
    return obj;
}

EmberObject *EmberObjectCreateDecimal(float value) {
    EmberObject *obj = EmberObjectCreate("Decimal");
    float *data = malloc(sizeof(float));
    memcpy(data, &value, sizeof(float));
	obj->typeFlags = TYPE_DECIMAL;
    EmberDictionaryPut(obj->attributes, "value", data);
    return obj;
}

EmberObject *EmberObjectCreateInteger(int value) {
    EmberObject *obj = EmberObjectCreate("Integer");
    int *data = malloc(sizeof(int));
    memcpy(data, &value, sizeof(int));
	obj->typeFlags = TYPE_INTEGER;
    EmberDictionaryPut(obj->attributes, "value", data);
    return obj;
}

int EmberIntegerValue(EmberObject *obj) {
	EmberObject *attrib = EmberObjectGetAttribute(obj, "value");
	return *(int*)attrib;
}

void get_values(int *int1, int *int2) {
    EmberObject *obj = NULL, *arg = NULL;
    void *value1, *value2;

    obj = EmberCallStackRetrieve("this");
    arg = EmberCallStackRetrieve("arg");

    value1 = EmberObjectGetAttribute(obj, "value");
    value2 = EmberObjectGetAttribute(arg, "value");

    *int1 = *(int*)value1;
    *int2 = *(int*)value2;
}

EmberObject *ember_integer_add() {
    int int1, int2;
    get_values(&int1, &int2);
    return EmberObjectCreateInteger(int1 + int2);
}

EmberObject *ember_integer_subtract() {
    int int1, int2;
    get_values(&int1, &int2);
    return EmberObjectCreateInteger(int1 - int2);
}

EmberObject *ember_integer_multiply() {
    int int1, int2;
    get_values(&int1, &int2);
    return EmberObjectCreateInteger(int1 * int2);
}

EmberObject *ember_integer_divide() {
    int int1, int2;
    get_values(&int1, &int2);
    return EmberObjectCreateInteger(int1 / int2);
}

EmberObject *ember_integer_compare() {
    return ember_integer_subtract();
}

EmberObject *ember_integer_equal_to() {
	return EmberObjectCreateInteger(EmberIntegerValue(ember_integer_compare()) == 0);
}

EmberObject *ember_integer_not_equal_to() {
	return EmberObjectCreateInteger(EmberIntegerValue(ember_integer_compare()) != 0);
}

EmberObject *ember_integer_less_than_or_equal_to() {
	return EmberObjectCreateInteger(EmberIntegerValue(ember_integer_compare()) <= 0);
}

EmberObject *ember_integer_greater_than_or_equal_to() {
	return EmberObjectCreateInteger(EmberIntegerValue(ember_integer_compare()) >= 0);
}

EmberObject *ember_integer_less_than() {
	return EmberObjectCreateInteger(EmberIntegerValue(ember_integer_compare()) < 0);
}

EmberObject *ember_integer_boolean_and() {
	int int1, int2;
    get_values(&int1, &int2);
	return EmberObjectCreateInteger(int1 && int2);
}

EmberObject *ember_integer_boolean_or() {
	int int1, int2;
    get_values(&int1, &int2);
	return EmberObjectCreateInteger(int1 || int2);
}

EmberObject *ember_integer_greater_than() {
	return EmberObjectCreateInteger(EmberIntegerValue(ember_integer_compare()) > 0);
}

EmberObject *ember_integer_increment() {
	EmberObject *ths = EmberCallStackRetrieve("this");
	int *value = (int*)EmberObjectGetAttribute(ths, "value");
	*value = (*value) + 1;
	return ths;
}

EmberObject *ember_integer_decrement() {
	EmberObject *ths = EmberCallStackRetrieve("this");
	int *value = (int*)EmberObjectGetAttribute(ths, "value");
	*value = (*value) - 1;
	return ths;
}



EmberObject *ember_integer_to_string() {
    EmberObject *obj = EmberCallStackRetrieve("this");
    int *attrib = EmberObjectGetAttribute(obj, "value");
    return EmberObjectCreateString(EmberStringFromInt(*attrib));
}

float EmberDecimalValue(EmberObject *obj) {
	EmberObject *attrib = EmberObjectGetAttribute(obj, "value");
	return *(float*)attrib;
}

void get_float_values(float *int1, float *int2) {
    EmberObject *obj = NULL, *arg = NULL;
    void *value1, *value2;

    obj = EmberCallStackRetrieve("this");
    arg = EmberCallStackRetrieve("arg");

    value1 = EmberObjectGetAttribute(obj, "value");
    value2 = EmberObjectGetAttribute(arg, "value");

    *int1 = *(float*)value1;
    *int2 = *(float*)value2;
}

EmberObject *ember_decimal_add() {
    float float1, float2;
    get_float_values(&float1, &float2);
    return EmberObjectCreateDecimal(float1 + float2);
}

EmberObject *ember_decimal_subtract() {
    float float1, float2;
    get_float_values(&float1, &float2);
    return EmberObjectCreateDecimal(float1 - float2);
}

EmberObject *ember_decimal_multiply() {
    float float1, float2;
    get_float_values(&float1, &float2);
    return EmberObjectCreateDecimal(float1 * float2);
}

EmberObject *ember_decimal_divide() {
    float float1, float2;
    get_float_values(&float1, &float2);
    return EmberObjectCreateDecimal(float1 / float2);
}

EmberObject *ember_decimal_compare() {
    return ember_decimal_subtract();
}

EmberObject *ember_decimal_equal_to() {
	return EmberObjectCreateInteger(EmberDecimalValue(ember_decimal_compare()) == 0);
}

EmberObject *ember_decimal_not_equal_to() {
	return EmberObjectCreateInteger(EmberDecimalValue(ember_decimal_compare()) != 0);
}

EmberObject *ember_decimal_less_than_or_equal_to() {
	return EmberObjectCreateInteger(EmberDecimalValue(ember_decimal_compare()) <= 0);
}

EmberObject *ember_decimal_greater_than_or_equal_to() {
	return EmberObjectCreateInteger(EmberDecimalValue(ember_decimal_compare()) >= 0);
}

EmberObject *ember_decimal_less_than() {
	return EmberObjectCreateInteger(EmberDecimalValue(ember_decimal_compare()) < 0);
}

EmberObject *ember_decimal_greater_than() {
	return EmberObjectCreateInteger(EmberDecimalValue(ember_decimal_compare()) > 0);
}

EmberObject *ember_decimal_to_string() {
    EmberObject *obj = EmberCallStackRetrieve("this");
    EmberObject *attrib = EmberObjectGetAttribute(obj, "value");
    float value = *(float*)attrib;
    char *buffer = EmberStringCreate(10);
    sprintf(buffer, "%f", value);
    return EmberObjectCreateString(buffer);
}

void get_string_values(char **string1, char **string2) {
    EmberObject *obj, *arg;
    void *value1, *value2;

    obj = EmberCallStackRetrieve("this");
    arg = EmberCallStackRetrieve("arg");

    value1 = EmberObjectGetAttribute(obj, "value");
    value2 = EmberObjectGetAttribute(arg, "value");

    *string1 = (char*)value1;
    *string2 = (char*)value2;
}


EmberObject *ember_string_compare() {
	char *string1, *string2;
	get_string_values(&string1, &string2);
	return EmberObjectCreateInteger(strcmp(string1, string2));
}

EmberObject *ember_string_add() {
	char *string1, *string2, *result;
	get_string_values(&string1, &string2);
	result = EmberStringConcat(string1, string2);
	return EmberObjectCreateString(result);
}

EmberObject *ember_string_to_string() {
	return EmberCallStackRetrieve("this");
}

char* EmberStringValue(EmberObject *obj) {
	char *attrib = EmberObjectGetAttribute(obj, "value");
	return attrib;
}

void EmberDecimalInstall() {
	EmberClassDef *classdef;
    classdef = EmberClassCreate("Decimal", NULL);
    EmberDictionaryPut(classdef->instMethods, "add", EmberMethodCreate("arg", 1, ember_decimal_add));
	EmberDictionaryPut(classdef->instMethods, "multiply", EmberMethodCreate("arg", 1, ember_decimal_multiply));
	EmberDictionaryPut(classdef->instMethods, "divide", EmberMethodCreate("arg", 1, ember_decimal_divide));
	EmberDictionaryPut(classdef->instMethods, "subtract", EmberMethodCreate("arg", 1, ember_decimal_subtract));
	EmberDictionaryPut(classdef->instMethods, "compare", EmberMethodCreate("arg", 1, ember_decimal_compare));
	EmberDictionaryPut(classdef->instMethods, "greaterThan", EmberMethodCreate("arg", 1, ember_decimal_greater_than));
	EmberDictionaryPut(classdef->instMethods, "lessThan", EmberMethodCreate("arg", 1, ember_decimal_less_than));
	EmberDictionaryPut(classdef->instMethods, "equalTo", EmberMethodCreate("arg", 1, ember_decimal_equal_to));
	EmberDictionaryPut(classdef->instMethods, "notEqualTo", EmberMethodCreate("arg", 1, ember_decimal_not_equal_to));
	EmberDictionaryPut(classdef->instMethods, "greaterThanOrEqualTo", EmberMethodCreate("arg", 1, ember_decimal_greater_than_or_equal_to));
	EmberDictionaryPut(classdef->instMethods, "lessThanOrEqualTo", EmberMethodCreate("arg", 1, ember_decimal_less_than_or_equal_to));
	EmberDictionaryPut(classdef->instMethods, "toString", EmberMethodCreate("", 1, ember_decimal_to_string));
	EmberClassMapPut(classdef);
}

void EmberStringInstall() {
	EmberClassDef *classdef;
    classdef = EmberClassCreate("String", NULL);
    EmberDictionaryPut(classdef->instMethods, "add", EmberMethodCreate("arg", 1, ember_string_add));
	EmberDictionaryPut(classdef->instMethods, "compare", EmberMethodCreate("arg", 1, ember_string_compare));
	EmberDictionaryPut(classdef->instMethods, "toString", EmberMethodCreate("", 1, ember_string_to_string));
	EmberClassMapPut(classdef);
}

void EmberNullInstall() {
	EmberClassDef *classdef;
	classdef = EmberClassCreate("Null", NULL);
	EmberClassMapPut(classdef);

	emberNull = EmberObjectCreate("Null");
}

EmberObject *ember_object_to_string() {
	EmberObject *ths = EmberCallStackRetrieve("this");
	char *string = EmberStringCopy("<");
	EmberStringAppend(&string, ths->classdef->name);
	EmberStringAppend(&string, ": 0x");
	EmberStringAppend(&string, EmberStringFromInt((long)ths));
	EmberStringAppend(&string, ">");
	return EmberObjectCreateString(string);
}

EmberObject *ember_object_equal_to() {
	EmberObject *ths = EmberCallStackRetrieve("this");
	EmberObject *arg = EmberCallStackRetrieve("arg");
	return EmberObjectCreateInteger(ths == arg);
}

EmberObject *ember_object_not_equal_to() {
	EmberObject *ths = EmberCallStackRetrieve("this");
	EmberObject *arg = EmberCallStackRetrieve("arg");
	return EmberObjectCreateInteger(ths != arg);
}

void EmberObjectInstall() {
	EmberClassDef *classdef = EmberClassCreate("Object", NULL);
	EmberDictionaryPut(classdef->instMethods, "toString", EmberMethodCreate("", 1, ember_object_to_string));
	EmberDictionaryPut(classdef->instMethods, "equalTo", EmberMethodCreate("arg", 1, ember_object_equal_to));
	EmberDictionaryPut(classdef->instMethods, "notEqualTo", EmberMethodCreate("arg", 1, ember_object_not_equal_to));
	EmberClassMapPut(classdef);
}

void EmberIntegerInstall() {
    EmberClassDef *classdef;
    classdef = EmberClassCreate("Integer", NULL);
    EmberDictionaryPut(classdef->instMethods, "add", EmberMethodCreate("arg", 1, ember_integer_add));
	EmberDictionaryPut(classdef->instMethods, "multiply", EmberMethodCreate("arg", 1, ember_integer_multiply));
	EmberDictionaryPut(classdef->instMethods, "divide", EmberMethodCreate("arg", 1, ember_integer_divide));
	EmberDictionaryPut(classdef->instMethods, "subtract", EmberMethodCreate("arg", 1, ember_integer_subtract));
	EmberDictionaryPut(classdef->instMethods, "compare", EmberMethodCreate("arg", 1, ember_integer_compare));
	EmberDictionaryPut(classdef->instMethods, "greaterThan", EmberMethodCreate("arg", 1, ember_integer_greater_than));
	EmberDictionaryPut(classdef->instMethods, "lessThan", EmberMethodCreate("arg", 1, ember_integer_less_than));
	EmberDictionaryPut(classdef->instMethods, "equalTo", EmberMethodCreate("arg", 1, ember_integer_equal_to));
	EmberDictionaryPut(classdef->instMethods, "notEqualTo", EmberMethodCreate("arg", 1, ember_integer_not_equal_to));
	EmberDictionaryPut(classdef->instMethods, "booleanAnd", EmberMethodCreate("arg", 1, ember_integer_boolean_and));
	EmberDictionaryPut(classdef->instMethods, "booleanOr", EmberMethodCreate("arg", 1, ember_integer_boolean_or));
	EmberDictionaryPut(classdef->instMethods, "greaterThanOrEqualTo", EmberMethodCreate("arg", 1, ember_integer_greater_than_or_equal_to));
	EmberDictionaryPut(classdef->instMethods, "lessThanOrEqualTo", EmberMethodCreate("arg", 1, ember_integer_less_than_or_equal_to));
	EmberDictionaryPut(classdef->instMethods, "increment", EmberMethodCreate("", 1, ember_integer_increment));
	EmberDictionaryPut(classdef->instMethods, "decrement", EmberMethodCreate("", 1, ember_integer_decrement));
	EmberDictionaryPut(classdef->instMethods, "toString", EmberMethodCreate("", 1, ember_integer_to_string));
	EmberClassMapPut(classdef);
}
